import React from 'react';
import { useEffect, useMemo, useCallback, useReducer } from 'react';
import { Table, Pagination, Dropdown, Button, Menu, Drawer, Tree, message } from 'antd';
import { fetch, exporData } from '@/services/commCRUD';
import { useModel, history } from 'umi';
import _ from 'lodash';
import { SearchOutlined, DownOutlined } from '@ant-design/icons';
import FilterItem from './FilterItem';
import type { SorterResult } from 'antd/lib/table/interface';
import { setState } from '@/utils/common';
// 属性类型
type PropField = {
  /** 后台dao的名称 */
  daoName: string;
  /** column列表 */
  columns: API.Column[];
  /** controls列表，用于列头过滤，column配置dataType时可以不配置 */
  controls?: API.Control[];
  /** 标题 */
  title?: string;
  /** 宽度，一般按照columns的宽度自动生成 */
  width?: number;
  /** 高度，一般按照窗口的高度自动生成 */
  height?: number;
  /** 表格是否有边框，默认:true */
  bordered?: boolean;
  /** 表格尺寸,默认:middle*/
  size?: 'default' | 'middle' | 'small';
  /** 每页显示的数据条数 */
  pageSize: number;
  /** 是否可以分页 */
  canPage?: boolean;
  /** 是否可以选择每页显示条数 */
  canPageSize?: boolean;
  /** 是否可以选择显示的项目 */
  canSelectItems?: boolean;
  /** 是否显示清除过滤条件按钮 */
  canClearFilter?: boolean;
  /** 是否可以导出 */
  canExport?: boolean;
  /** 是否可以选择行 */
  canSelectRow?: boolean;
  /** 是否可以多选 */
  multiSelect?: boolean;
  /** 主键列的名称，用于生成react.Key，必须配置正确 */
  keyColName?: string;
  /** 查询条件 */
  seeks?: API.WhereCause[];
  /** 初始排序 */
  initSorter?: API.OrderItem;
  /** 表格action引用，可以用于表格的刷新,排序,获取rowDatas */
  actionRef?: React.MutableRefObject<API.TableAction | undefined>;
  /** 选择改变时触发的回调 */
  onSelectChange?: (selKeys: React.Key[], selRows: Record<string, any>[]) => Promise<void>;
  /** 数据改变时触发的回调 */
  onDataChange?: (rows: Record<string, any>[]) => Promise<void>;
};
type StateField = {
  listData?: API.ListData;
  columns?: API.Column[];
  width?: number;
  curPage?: number;
  pageSize?: number;
  sorter?: API.OrderItem[];
  filters?: API.WhereCause[];
  selKeys?: React.Key[];
  visibleFilterName?: string;
  visibleItemDrawer?: boolean;
  showColNames?: string[];
  loading?: boolean;
};
const DataTable: React.FC<PropField> = (props) => {
  const initialState = {
    listData: undefined,
    columns: undefined,
    width: 0,
    curPage: 1,
    pageSize: props.pageSize,
    sorter: undefined,
    filters: [],
    selKeys: [],
    visibleFilterName: '',
    visibleItemDrawer: false,
    showColNames: [],
    loading: false,
  };
  // const pagepath = history.location.pathname;
  // const localState = getState(pagepath, props.daoName);
  // if (localState !== null) {
  //   initialState = { ...initialState, ...localState };
  // }
  const [state, dispatch] = useReducer((prevState: StateField, newState: StateField) => {
    return { ...prevState, ...newState };
  }, initialState);
  const { bodyHeight, curPermID } = useModel('sysInfo');
  // useEffect(() => {
  //   dispatch({ filters: [], curPage: 1 });
  // }, [props.seeks]);
  useEffect(() => {
    const scns: string[] = [];
    const loop = (cols: API.Column[]) =>
      cols.forEach((item) => {
        if (item.children) {
          loop(item.children);
        }
        if (item.dispaly) {
          scns.push(item.key!);
        }
      });
    loop(props.columns);
    dispatch({
      showColNames: scns,
      sorter: props.initSorter ? [props.initSorter!] : [],
    });
  }, [props.columns, props.initSorter]);
  useEffect(() => {
    if (_.isEmpty(state.showColNames)) {
      return;
    }
    const cols: API.Column[] = [];
    let wd: number = 0;
    const getCols = (c: API.Column) => {
      if (c.children) {
        const children: API.Column[] = [];
        const ncol = { ...c, children };
        c.children.map((ch) => {
          const cc = getCols(ch);
          if (cc) {
            ncol.children.push(cc);
          }
        });
        return ncol;
      } else {
        const nc = { ...c };
        if (state.showColNames!.indexOf(nc.key!) !== -1) {
          wd += nc.width === undefined ? 120 : nc.width;
          if (nc.filter) {
            let cont = _.isEmpty(props.controls)
              ? undefined
              : props.controls!.find((p) => p.name === nc.key);
            const filterDatas = state.filters!.find(
              (f) => !_.isEmpty(f.WhereItems) && f.WhereItems[0].FieldName === nc.key,
            );
            if (cont === undefined) {
              if (nc.dataType === undefined) {
                message.error(
                  `controls中不存在${nc.key},请指定列上的dataType属性或者修改filter为false`,
                );
                return;
              }
              let name;
              if (nc.dataIndex instanceof Array) {
                name = nc.dataIndex.join('.');
              } else {
                name = nc.dataIndex;
              }
              cont = {
                label: nc.title,
                name: name!,
                type: nc.dataType === 'string' ? 'text' : (nc.dataType as API.ControlType),
                dataType: nc.dataType,
              };
            }
            let visible = false;
            if (nc.key === state.visibleFilterName) {
              visible = true;
            }
            nc.filterDropdown = (
              <FilterItem
                control={cont!}
                filters={filterDatas}
                visible={visible}
                onOk={async (filter) => {
                  const idx = state.filters!.findIndex(
                    (f) =>
                      f.WhereItems[0] &&
                      filter.WhereItems[0] &&
                      f.WhereItems[0].FieldName === filter.WhereItems[0].FieldName,
                  );
                  const newfilters = [...state.filters!];
                  if (idx !== -1) {
                    newfilters[idx] = filter;
                  } else {
                    newfilters.push(filter);
                  }
                  dispatch({ filters: newfilters, curPage: 1 });
                }}
                onCancel={() => dispatch({ visibleFilterName: '' })}
                onClear={(name) => {
                  const idx = state.filters!.findIndex(
                    (f) => f.WhereItems[0] && f.WhereItems[0].FieldName === name,
                  );
                  if (idx !== -1) {
                    const newfilters = [...state.filters!];
                    newfilters.splice(idx);
                    dispatch({ filters: newfilters, curPage: 1 });
                  }
                }}
              />
            );
            const idx = state.filters!.findIndex(
              (f) => f.WhereItems[0] && f.WhereItems[0].FieldName === nc.key,
            );
            nc.filterDropdownVisible = visible;
            nc.filteredValue = idx !== -1 ? [1] : [];
            nc.filterIcon = <SearchOutlined />;
            if (idx !== -1) {
              nc.filterIcon = <SearchOutlined style={{ color: '#1890ff' }} />;
            }
            nc.onFilterDropdownVisibleChange = () => dispatch({ visibleFilterName: nc.key! });
          }
          return nc;
        }
        return null;
      }
    };
    props.columns.map((c) => {
      const cc = getCols(c);
      if (cc) {
        cols.push(cc);
      }
    });
    dispatch({ columns: cols, width: wd });
  }, [props.columns, props.controls, state.filters, state.showColNames, state.visibleFilterName]);
  const loadData = useCallback(() => {
    if (state.sorter === undefined) {
      return;
    }
    dispatch({ loading: true });
    fetch(
      props.daoName,
      state.curPage!,
      state.pageSize!,
      state.sorter!,
      [...state.filters!, ...props.seeks!],
      curPermID,
    )
      .then(function (data) {
        setState(history.location.pathname, props.daoName, {
          curPage: state.curPage,
          pageSize: state.pageSize,
          sorter: state.sorter,
          filters: state.filters,
        });
        if (props.onDataChange) {
          props.onDataChange(data.Objectlist);
        }
        dispatch({ listData: data, selKeys: [] });
      })
      .catch(function (ex) {
        console.error('请求', props.daoName, '错误', ex);
      })
      .finally(function () {
        dispatch({ loading: false });
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    state.sorter,
    state.curPage,
    state.pageSize,
    state.filters,
    curPermID,
    props.seeks,
    props.onDataChange,
  ]);
  useEffect(() => {
    loadData();
  }, [loadData]);
  const pageChangeHandler = (page: number) => {
    dispatch({ curPage: page });
  };
  const pageSizeChangeHandler = (pz: string) => {
    dispatch({ pageSize: Number(pz) });
  };
  const selectChangeHandler = (selRowKeys: React.Key[], selRows: object[]) => {
    dispatch({ selKeys: selRowKeys });
    if (props.onSelectChange) {
      props.onSelectChange(selRowKeys, selRows);
    }
  };
  const handleSorterChange = useCallback(
    (order: SorterResult<object>) => {
      if (order) {
        const ccol = props.columns.find((col) => col.key === order.columnKey);
        const s: API.OrderItem = {
          FieldName:
            ccol!.dataIndex instanceof Array ? ccol!.dataIndex!.join('.') : ccol!.dataIndex!,
          Direction: order.order === 'ascend' ? 'ASC' : 'DESC',
        };
        dispatch({ sorter: [s] });
      }
    },
    [props.columns],
  );
  const exportDataToXlsx = () => {
    if (_.isEmpty(state.columns)) {
      return;
    }
    const getColumns = (c: API.Column) => {
      if (c.children) {
        const chs: API.ColumnHeader[] = [];
        c.children.forEach((ch) => {
          const head = getColumns(ch);
          if (head !== undefined) {
            chs.push(head);
          }
        });
        if (chs.length === 0) {
          return undefined;
        }
        return { Caption: c.title, Children: chs };
      } else {
        if (c.dataIndex && state.showColNames!.findIndex((cName) => cName === c.key) !== -1) {
          const name = c.dataIndex instanceof Array ? c.dataIndex.join('.') : c.dataIndex;
          const header: API.ColumnHeader = {
            Name: name,
            Caption: c.title,
            Width: c.width,
          };
          return header;
        }
        return undefined;
      }
    };
    const columnHeaders: API.ColumnHeader[] = [];
    state.columns!.forEach((c) => {
      const head = getColumns(c);
      if (head !== undefined) {
        columnHeaders.push(head);
      }
    });
    if (columnHeaders.length === 0) {
      return;
    }
    exporData(
      props.daoName,
      [...state.filters!, ...props.seeks!],
      state.sorter!,
      columnHeaders,
      curPermID,
    )
      .then(function (data) {
        const a = document.createElement('a');
        const url = window.URL.createObjectURL(data);
        a.href = url;
        const fName = props.title === '' ? '数据导出' : props.title;
        a.download = `${fName}.xlsx`;
        document.body.appendChild(a);
        a.click();
        setTimeout(function () {
          document.body.removeChild(a);
        }, 1000);
        window.URL.revokeObjectURL(url);
      })
      .catch(function (ex) {
        console.error('导出', props.daoName, '数据错误', ex);
      });
  };
  const clearFilter = () => {
    dispatch({ filters: [], curPage: 1 });
  };
  const rowClickHandler = (record: Record<string, any>) => {
    return {
      onClick: (e: any) => {
        e.stopPropagation();
        const key = props.keyColName!;
        const val: number = record[key] as number;
        if (props.multiSelect) {
          const newKeys = [...state.selKeys!];
          const index = newKeys.indexOf(val);
          if (index === -1) {
            newKeys.push(val);
          } else {
            newKeys.splice(index, 1);
          }
          const selRows: object[] = [];
          state.listData?.Objectlist.forEach((item) => {
            if (newKeys.findIndex((kv) => item[key] === kv) !== -1) {
              selRows.push(item);
            }
          });
          selectChangeHandler(newKeys, selRows);
        } else {
          selectChangeHandler([val], [record]);
        }
      },
    };
  };
  useEffect(() => {
    if (!_.isEmpty(props.actionRef)) {
      props.actionRef!.current = {
        reload: loadData,
        sort: handleSorterChange,
        rowDatas: state.listData?.Objectlist,
        selKeys: state.selKeys,
      };
    }
    return () => {
      if (!_.isEmpty(props.actionRef)) {
        props.actionRef!.current = undefined;
      }
    };
  }, [loadData, props.actionRef, handleSorterChange, state.listData?.Objectlist, state.selKeys]);
  const selTree = useMemo(() => {
    const buildTree = (cols: API.Column[], pkey: string) =>
      cols.map((item) => {
        if (item.children) {
          const key = pkey + '_' + item.title;
          return (
            <Tree.TreeNode key={key} title={item.title}>
              {buildTree(item.children, key)}
            </Tree.TreeNode>
          );
        }
        if (_.isEmpty(item.title)) {
          return null;
        }
        return <Tree.TreeNode key={item.key} title={item.title} />;
      });
    return buildTree(props.columns!, '');
  }, [props.columns]);
  const getColumnDeep = (col: API.Column) => {
    if (_.isEmpty(col.children)) {
      return 1;
    }
    let deep = 0;
    col.children?.forEach((ccol) => {
      const d2 = getColumnDeep(ccol);
      if (d2 > deep) {
        deep = d2;
      }
    });
    return deep + 1;
  };
  if (_.isEmpty(state.columns)) {
    return <></>;
  }
  let x: number = props.width!;
  let y: number = props.height!;
  if (x === 0) {
    x = state.width!;
  }
  if (y === 0) {
    let maxDeep = 0;
    state.columns!.forEach((c) => {
      const dp = getColumnDeep(c);
      if (dp > maxDeep) {
        maxDeep = dp;
      }
    });
    y = bodyHeight - 235 - (maxDeep - 1) * 40;
  }
  const menu2 = (
    <Menu onClick={(e) => pageSizeChangeHandler(e.key)}>
      <Menu.Item key="10">每页显示10行</Menu.Item>
      <Menu.Item key="20">每页显示20行</Menu.Item>
      <Menu.Item key="30">每页显示30行</Menu.Item>
      <Menu.Item key="40">每页显示40行</Menu.Item>
      <Menu.Item key="50">每页显示50行</Menu.Item>
    </Menu>
  );
  return (
    <div>
      <Table
        columns={state.columns}
        dataSource={state.listData?.Objectlist}
        loading={state.loading}
        onRow={rowClickHandler}
        rowKey={(record) => record[props.keyColName!]}
        scroll={{ x, y }}
        bordered={props.bordered}
        rowSelection={
          props.canSelectRow
            ? {
                selectedRowKeys: state.selKeys,
                onChange: selectChangeHandler,
                selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],
                type: props.multiSelect ? 'checkbox' : 'radio',
              }
            : undefined
        }
        pagination={false}
        size="small"
        onChange={(_t, _f, s) => handleSorterChange(s as SorterResult<object>)}
      />
      <div>
        {props.canPageSize ? (
          <Dropdown overlay={menu2} trigger={['click']}>
            <Button type="ghost" style={{ margin: 5 }}>
              每页显示{state.pageSize}行 <DownOutlined />
            </Button>
          </Dropdown>
        ) : null}
        {props.canSelectItems ? (
          <Button
            type="ghost"
            style={{ margin: 5 }}
            onClick={() => dispatch({ visibleItemDrawer: true })}
          >
            显示项目 <DownOutlined />
          </Button>
        ) : null}
        {props.canExport ? (
          <Button style={{ margin: 5 }} key="b_exportData" onClick={exportDataToXlsx}>
            导出excel
          </Button>
        ) : null}
        {props.canClearFilter ? (
          <Button style={{ margin: 5 }} key="b_clearFilter" onClick={clearFilter}>
            清除过滤条件
          </Button>
        ) : null}
        {props.canPage ? (
          <Pagination
            style={{ margin: '5px 5px', float: 'right' }}
            className="ant-table-pagination"
            total={state.listData?.Count}
            current={state.curPage}
            pageSize={state.pageSize}
            onChange={pageChangeHandler}
            showTotal={(total) => (total ? `共 ${total} 行` : '')}
            showSizeChanger={false}
          />
        ) : null}
        <Drawer
          title="选择要显示的项目"
          placement="right"
          width={350}
          closable={true}
          onClose={() => dispatch({ visibleItemDrawer: false })}
          visible={state.visibleItemDrawer}
        >
          <Tree
            checkable={true}
            onCheck={(checked) => dispatch({ showColNames: checked as string[] })}
            checkedKeys={state.showColNames}
          >
            {selTree}
          </Tree>
        </Drawer>
      </div>
    </div>
  );
};
export default DataTable;
DataTable.defaultProps = {
  bordered: true,
  size: 'middle',
  canPage: true,
  canPageSize: true,
  canSelectItems: true,
  canClearFilter: true,
  canSelectRow: true,
  canExport: true,
  pageSize: 20,
  width: 0,
  height: 0,
  seeks: [],
  keyColName: 'ID',
  title: '',
};
